package crp.core.security.domain.services;

import com.qwlabs.cdi.DispatchInstance;
import crp.core.security.domain.messages.SecurityMessages;
import crp.core.security.domain.models.SSOAuthenticationRequest;
import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.IdentityProvider;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.request.AnonymousAuthenticationRequest;
import io.quarkus.security.runtime.AnonymousIdentityProvider;
import io.smallrye.mutiny.Uni;
import lombok.extern.slf4j.Slf4j;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;

@ApplicationScoped
@Slf4j
public class SSOIdentityProviderDispatcher implements IdentityProvider<SSOAuthenticationRequest> {
    private final DispatchInstance<String, SSOIdentityProvider> dispatcher;
    private final AnonymousIdentityProvider anonymousIdentityProvider = new AnonymousIdentityProvider();

    @Inject
    public SSOIdentityProviderDispatcher(Instance<SSOIdentityProvider> instance) {
        this.dispatcher = DispatchInstance.of(instance);
    }

    @Override
    public Class<SSOAuthenticationRequest> getRequestType() {
        return SSOAuthenticationRequest.class;
    }

    @Override
    public Uni<SecurityIdentity> authenticate(SSOAuthenticationRequest request, AuthenticationRequestContext context) {
        try {
            return doAuthenticate(request, context);
        } catch (Exception e) {
            LOGGER.warn("sso login failed.", e);
            throw SecurityMessages.INSTANCE.ssoLoginFailed(request.getProvider());
        }
    }

    private Uni<SecurityIdentity> doAuthenticate(SSOAuthenticationRequest request, AuthenticationRequestContext context) {
        return dispatcher.getOptional(request.getProvider())
                .map(provider -> provider.authenticate(request, context))
                .orElseGet(() -> anonymousIdentityProvider.authenticate(AnonymousAuthenticationRequest.INSTANCE, context));
    }
}
